//
// Created by LONG on 2020/10/27.
//


#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "../../include/utils/wheelTimer.h"

#define TICKS_PER_WHEEL 1024

struct WtWheel
{
    uint32_t clock;
    S_List wheel[TICKS_PER_WHEEL];
};

struct WtTimer
{
    S_List node;
    uint32_t type;
    uint32_t delay;
    uint32_t remainRounds;
    uint32_t runState;
    WheelTimerCallback callback;
    void *arg;
};

S_WtWheel *WT_CreateWheel(const char *name)
{
    uint32_t loop;
    S_WtWheel *wheel;

    wheel = OS_MemCalloc(1, sizeof(S_WtWheel));
    if (wheel == NULL)
    {
        return NULL;
    }

    for (loop = 0; loop < TICKS_PER_WHEEL; loop++)
    {
        LIST_Init(&wheel->wheel[loop]);
    }

    return wheel;
}

void WT_DestroyWheel(S_WtWheel *wheel)
{
    uint32_t loop;

    for (loop = 0; loop < TICKS_PER_WHEEL; loop++)
    {
        LIST_Final(&wheel->wheel[loop]);
    }

    OS_MemFree(wheel);
}

S_WtTimer* WT_CreateTimer(void)
{
    return (S_WtTimer *)OS_MemCalloc(1, sizeof(S_WtTimer));;
}

void WT_DestroyTimer(S_WtTimer *timer)
{
    LIST_Remove(&timer->node);
    OS_MemFree(timer);
}

int32_t WT_StartTimer(S_WtWheel *wheel, S_WtTimer *timer, WheelTimerCallback callback, void *arg, uint32_t type, uint32_t delay)
{
    uint32_t clock;

    if (delay == 0 || type >= TIMER_TYPE_BUTT)
    {
        return CSP_RC_ER;
    }

    timer->type = type;
    timer->delay = delay;
    timer->callback = callback;
    timer->arg = arg;

    LIST_Remove(&timer->node);

    clock = wheel->clock + timer->delay;

    timer->runState = TIMER_STATE_START;
    timer->remainRounds = timer->delay / TICKS_PER_WHEEL;
    LIST_Append(&wheel->wheel[clock % TICKS_PER_WHEEL], &timer->node);

    return CSP_RC_OK;
}

int32_t WT_StopTimer(S_WtTimer *timer)
{
    timer->runState = TIMER_STATE_STOP;
    LIST_Remove(&timer->node);
    return CSP_RC_OK;
}

int32_t WT_TimerState(S_WtTimer *timer)
{
    return timer->runState;
}

void WT_Run(S_WtWheel *wheel, uint32_t step)
{
    uint32_t loop;
    S_List *list;
    S_List listTmp;
    S_List *currSlot;
    S_List currSlotTmp;
    S_WtTimer *timer;

    LIST_Init(&listTmp);
    LIST_Init(&currSlotTmp);

    for (loop = 0; loop < step; loop++)
    {
        wheel->clock++;
        currSlot = &wheel->wheel[wheel->clock % TICKS_PER_WHEEL];
        LIST_Join(&currSlotTmp, currSlot);
        while (!LIST_Empty(&currSlotTmp))
        {
            timer = LIST_FIRST_ENTRY(&currSlotTmp, S_WtTimer, node);
            LIST_Remove(&timer->node);
            if (timer->remainRounds)
            {
                timer->remainRounds--;
            }
            if (timer->remainRounds)
            {
                LIST_Append(&listTmp, &timer->node);
                continue;
            }
            timer->runState = TIMER_STATE_TIMEOUT;
            if (timer->callback)
            {
                timer->callback(timer->arg);
            }
            if (TIMER_TYPE_LOOP == timer->type
                && TIMER_STATE_TIMEOUT == timer->runState)
            {
                LIST_Append(&listTmp, &timer->node);
            }
        }
        while (!LIST_Empty(&listTmp))
        {
            timer = LIST_FIRST_ENTRY(&listTmp, S_WtTimer, node);
            LIST_Remove(&timer->node);
            if (TIMER_TYPE_LOOP == timer->type)
            {
                WT_StartTimer(wheel, timer, timer->callback, timer->arg, timer->type, timer->delay);
            }
            else
            {
                LIST_Append(currSlot, &timer->node);
            }
        }
    }
}